package com.smmisia.shop.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smmisia.common.response.PageResponse;
import com.smmisia.common.response.ResponseCode;
import com.smmisia.shop.entity.*;

import static com.smmisia.shop.exception.ProductErrorCode.*;

import com.smmisia.shop.entity.constant.ProductInventoryModifyType;
import com.smmisia.shop.entity.convetor.ProductConvertor;
import com.smmisia.shop.exception.ProductException;
import com.smmisia.shop.mapper.*;
import com.smmisia.shop.model.ProductVO;
import com.smmisia.shop.request.*;
import com.smmisia.shop.response.ProductCreateResponse;
import com.smmisia.shop.response.ProductInventoryModifyResponse;
import com.smmisia.shop.response.ProductPriceModifyResponse;
import com.smmisia.shop.response.ProductRemoveResponse;
import com.smmisia.shop.service.ProductCategoryService;
import com.smmisia.shop.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product>
        implements ProductService {

    @Autowired
    private ProductStreamMapper productStreamMapper;

    @Autowired
    private ProductInventoryStreamMapper productInventoryStreamMapper;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductCategoryService productCategoryService;

    @Autowired
    private BusinessProductMapper businessProductMapper;

    @Autowired
    private BusinessMapper businessMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProductCreateResponse saveProduct(ProductCreateRequest request) {
        Boolean existProductStream = existProductStream(request.getIdentifier());
        Assert.isFalse(existProductStream, () -> new ProductException(PRODUCT_STREAM_EXIST));
        productCategoryService.exitsCategory(Integer.valueOf(request.getClassId()));
        Product product = Product.create(request);
        boolean saveResult = this.save(product);
        Assert.isTrue(saveResult, () -> new ProductException(PRODUCT_SAVE_FAILED));
        ProductStream stream = new ProductStream(product, request.getIdentifier(), request.getEventType());
        saveResult = productStreamMapper.insert(stream) == 1;
        Assert.isTrue(saveResult, () -> new ProductException(PRODUCT_STREAM_SAVE_FAILED));
        BusinessProduct businessProduct = new BusinessProduct();
        businessProduct.setProductId(product.getId().intValue());
        businessProduct.setBusinessId(request.getBusinessId());
        int insert = businessProductMapper.insert(businessProduct);
        Assert.isTrue(insert == 1, () -> new ProductException(PRODUCT_SAVE_FAILED));
        ProductCreateResponse response = new ProductCreateResponse();
        response.setProductId(product.getId());
        response.setSuccess(true);
        response.setResponseCode("200");
        response.setResponseMessage("新增成功");
        return response;
    }

    @Override
    public Boolean existProductStream(String identifier) {
        LambdaQueryWrapper<ProductStream> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProductStream::getIdentifier, identifier);
        return productStreamMapper.selectOne(wrapper) != null;
    }


    @Override
    public PageResponse<ProductVO> pageQuery(ProductPageQueryRequest request) {
        Page<Product> page = new Page<>(request.getPageNum(), request.getPageSize());
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(request.getKeyword())) {
            wrapper.like("name", request.getKeyword());
        }
        Page<Product> productPage = this.page(page, wrapper);
        PageResponse<Product> response = PageResponse.of(productPage.getRecords(), (int) productPage.getTotal(), request.getPageNum(), request.getPageSize());
        PageResponse<ProductVO> pageResponse = PageResponse.of(ProductConvertor.INSTANCE.mapToVo(response.getDatas()), response.getTotal(), response.getPageSize(), request.getPageNum());
        pageResponse.setResponseMessage("查询成功");
        pageResponse.setResponseCode("200");
        return pageResponse;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ProductInventoryModifyResponse modifyInventory(ProductInventoryModifyRequest request) {
        ProductInventoryModifyResponse response = new ProductInventoryModifyResponse();
        response.setProductId(request.getProductId());
        ProductInventoryStream existStream = productInventoryStreamMapper.selectByIdentifier(request.getIdentifier(), request.getEventType().name(), request.getProductId());

        if (existStream != null) {
            response.setSuccess(true);
            response.setResponseCode(ResponseCode.DUPLICATED.name());
        }

        Product product = this.getById(request.getProductId());
        if (product == null) {
            throw new ProductException(PRODUCT_QUERY_FAIL);
        }

        long quantityDiff = request.getQuantity() - product.getQuantity();
        response.setQuantityModified(Math.abs(quantityDiff));

        if (quantityDiff == 0) {
            response.setModifyType(ProductInventoryModifyType.UNMODIFIED);
            response.setSuccess(true);
            return response;
        } else if (quantityDiff > 0) {
            response.setModifyType(ProductInventoryModifyType.INCREASE);
        } else {
            response.setModifyType(ProductInventoryModifyType.DECREASE);
        }

        long oldSaleableInventory = product.getSaleableInventory();
        product.setQuantity(request.getQuantity());
        product.setSaleableInventory(oldSaleableInventory + quantityDiff);

        boolean res = this.updateById(product);
        Assert.isTrue(res, () -> new ProductException(PRODUCT_UPDATE_FAILED));

        ProductInventoryStream inventoryStream = new ProductInventoryStream(product, request.getIdentifier(), request.getEventType(), quantityDiff);
        boolean saveResult = productInventoryStreamMapper.insert(inventoryStream) == 1;
        Assert.isTrue(saveResult, () -> new ProductException(PRODUCT_INVENTORY_UPDATE_FAILED));

        response.setSuccess(true);
        response.setResponseCode(ResponseCode.SUCCESS.name());
        response.setResponseMessage("库存更新成功");
        return response;
    }

    @Override
    public ProductVO queryById(Long productId) {
        Product product = getById(productId);
        return ProductConvertor.INSTANCE.mapToVo(product);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProductPriceModifyResponse modifyPrice(ProductModifyPriceRequest request) {
        ProductStream existStream = productStreamMapper.selectByIdentifier(request.getIdentifier(), request.getEventType().name(), request.getProductId());
        if (existStream != null) {
            throw new ProductException(PRODUCT_STREAM_EXIST);
        }

        Product product = getById(request.getProductId());
        product.setPrice(request.getPrice());
        boolean saveResult = updateById(product);
        Assert.isTrue(saveResult, () -> new ProductException(PRODUCT_SAVE_FAILED));

        ProductStream stream = new ProductStream(product, request.getIdentifier(), request.getEventType());
        saveResult = productStreamMapper.insert(stream) == 1;
        Assert.isTrue(saveResult, () -> new ProductException(PRODUCT_STREAM_SAVE_FAILED));
        ProductPriceModifyResponse response = new ProductPriceModifyResponse();
        response.setProductId(request.getProductId());
        response.setSuccess(true);
        response.setResponseCode("200");
        response.setResponseMessage("价格修改成功");
        return response;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProductRemoveResponse removeProduct(ProductRemoveRequest request) {
        ProductStream stream = productStreamMapper.selectByIdentifier(request.getIdentifier(), request.getEventType().name(), request.getProductId());
        if (stream != null) {
            throw new ProductException(PRODUCT_STREAM_EXIST);
        }
        Product product = getById(request.getProductId());
        product.remove();
        boolean saveResult = updateById(product);
        Assert.isTrue(saveResult, () -> new ProductException(PRODUCT_UPDATE_FAILED));
        ProductStream productStream = new ProductStream(product, request.getIdentifier(), request.getEventType());
        saveResult = productStreamMapper.insert(productStream) == 1;
        Assert.isTrue(saveResult, () -> new ProductException(PRODUCT_STREAM_SAVE_FAILED));
        businessMapper.selectOne(new QueryWrapper<Business>().eq("product_id", request.getProductId()));
        int result = businessMapper.deleteById(request.getProductId());
        Assert.isTrue(result == 1, () -> new ProductException(PRODUCT_UPDATE_FAILED));
        ProductRemoveResponse response = new ProductRemoveResponse();
        response.setProductId(request.getProductId());
        response.setSuccess(true);
        response.setResponseCode("200");
        response.setResponseMessage("商品删除成功");
        return response;
    }

    @Override
    public Boolean trySale(ProductTrySaleRequest request) {
        String loginId = (String) StpUtil.getLoginId();
        ProductInventoryStream exitsStream = productInventoryStreamMapper.selectByIdentifier(request.getIdentifier(), request.getEvent().name(), request.getProductId());
        if (exitsStream != null) {
            throw new ProductException(PRODUCT_STREAM_EXIST);
        }

        Product product = getById(request.getProductId());
        ProductInventoryStream stream = new ProductInventoryStream(product, request.getIdentifier(), request.getEvent(), request.getQuantity());
        int saveResult = productInventoryStreamMapper.insert(stream);
        Assert.isTrue(saveResult > 0, () -> new ProductException(PRODUCT_SAVE_FAILED));

        saveResult = productMapper.trySale(request.getProductId(), request.getQuantity());
        Assert.isTrue(saveResult == 1, () -> new ProductException(PRODUCT_SAVE_FAILED));

        return true;
    }

    @Override
    public Boolean cancelSale(ProductCancelSaleRequest request) {
        ProductInventoryStream exitsStream = productInventoryStreamMapper.selectByIdentifier(request.getIdentifier(), request.getEvent().name(), request.getProductId());
        if (exitsStream != null) {
            throw new ProductException(PRODUCT_STREAM_EXIST);
        }

        Product product = getById(request.getProductId());
        ProductInventoryStream stream = new ProductInventoryStream(product, request.getIdentifier(), request.getEvent(), request.getQuantity());
        int saveResult = productInventoryStreamMapper.insert(stream);
        Assert.isTrue(saveResult > 0, () -> new ProductException(PRODUCT_SAVE_FAILED));

        saveResult = productMapper.cancelSale(request.getProductId(), request.getQuantity());
        log.info("cancelSale", saveResult);
        Assert.isTrue(saveResult == 1, () -> new ProductException(PRODUCT_SAVE_FAILED));
        return true;
    }


}




